"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.promise");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _logger = _interopRequireDefault(require("./utils/logger.js"));

var _browser = _interopRequireDefault(require("./utils/browser.js"));

var _loader = require("./loader.js");

var _exception = require("./utils/exception.js");

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

/* fetch + stream IO loader. Currently working on chrome 43+.
 * fetch provides a better alternative http API to XMLHttpRequest
 *
 * fetch spec   https://fetch.spec.whatwg.org/
 * stream spec  https://streams.spec.whatwg.org/
 */
var FetchStreamLoader = /*#__PURE__*/function (_BaseLoader) {
  (0, _inherits2.default)(FetchStreamLoader, _BaseLoader);

  var _super = _createSuper(FetchStreamLoader);

  (0, _createClass2.default)(FetchStreamLoader, null, [{
    key: "isSupported",
    value: function isSupported() {
      try {
        // fetch + stream is broken on Microsoft Edge. Disable before build 15048.
        // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/
        // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist.
        var isWorkWellEdge = _browser.default.msedge && _browser.default.version.minor >= 15048;
        var browserNotBlacklisted = _browser.default.msedge ? isWorkWellEdge : true;
        return typeof fetch != 'undefined' && typeof ReadableStream != 'undefined' && browserNotBlacklisted;
      } catch (e) {
        return false;
      }
    }
  }]);

  function FetchStreamLoader(seekHandler, config) {
    var _this;

    (0, _classCallCheck2.default)(this, FetchStreamLoader);
    _this = _super.call(this, 'fetch-stream-loader');
    _this.TAG = 'FetchStreamLoader';
    _this._seekHandler = seekHandler;
    _this._config = config;
    _this._needStash = true;
    _this._requestAbort = false;
    _this._contentLength = null;
    _this._receivedLength = 0;
    return _this;
  }

  (0, _createClass2.default)(FetchStreamLoader, [{
    key: "destroy",
    value: function destroy() {
      if (this.isWorking()) {
        this.abort();
      }

      (0, _get2.default)((0, _getPrototypeOf2.default)(FetchStreamLoader.prototype), "destroy", this).call(this);
    }
  }, {
    key: "open",
    value: function open(dataSource, range) {
      var _this2 = this;

      this._dataSource = dataSource;
      this._range = range;
      var sourceURL = dataSource.url;

      if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
        sourceURL = dataSource.redirectedURL;
      }

      var seekConfig = this._seekHandler.getConfig(sourceURL, range);

      var headers = new self.Headers();

      if ((0, _typeof2.default)(seekConfig.headers) === 'object') {
        var configHeaders = seekConfig.headers;

        for (var key in configHeaders) {
          if (configHeaders.hasOwnProperty(key)) {
            headers.append(key, configHeaders[key]);
          }
        }
      }

      var params = {
        method: 'GET',
        headers: headers,
        mode: 'cors',
        cache: 'default',
        // The default policy of Fetch API in the whatwg standard
        // Safari incorrectly indicates 'no-referrer' as default policy, fuck it
        referrerPolicy: 'no-referrer-when-downgrade'
      }; // add additional headers

      if ((0, _typeof2.default)(this._config.headers) === 'object') {
        for (var _key in this._config.headers) {
          headers.append(_key, this._config.headers[_key]);
        }
      } // cors is enabled by default


      if (dataSource.cors === false) {
        // no-cors means 'disregard cors policy', which can only be used in ServiceWorker
        params.mode = 'same-origin';
      } // withCredentials is disabled by default


      if (dataSource.withCredentials) {
        params.credentials = 'include';
      } // referrerPolicy from config


      if (dataSource.referrerPolicy) {
        params.referrerPolicy = dataSource.referrerPolicy;
      } // abort control


      if (self.AbortController) {
        this._abortController = new self.AbortController();
        params.signal = this._abortController.signal;
      }

      this._status = _loader.LoaderStatus.kConnecting;
      self.fetch(seekConfig.url, params).then(function (res) {
        if (_this2._requestAbort) {
          _this2._requestAbort = false;
          _this2._status = _loader.LoaderStatus.kIdle;
          res.body.cancel();
          return;
        }

        if (res.ok && res.status >= 200 && res.status <= 299) {
          if (res.url !== seekConfig.url) {
            if (_this2._onURLRedirect) {
              var redirectedURL = _this2._seekHandler.removeURLParameters(res.url);

              _this2._onURLRedirect(redirectedURL);
            }
          }

          var lengthHeader = res.headers.get('Content-Length');

          if (lengthHeader != null) {
            _this2._contentLength = parseInt(lengthHeader);

            if (_this2._contentLength !== 0) {
              if (_this2._onContentLengthKnown) {
                _this2._onContentLengthKnown(_this2._contentLength);
              }
            }
          }

          _this2._response = res;
          return _this2._pump.call(_this2, res.body.getReader());
        } else {
          _this2._status = _loader.LoaderStatus.kError;

          if (_this2._requestAbort) {
            return;
          }

          if (_this2._onError) {
            _this2._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, {
              code: res.status,
              msg: res.statusText
            });
          } else {
            throw new _exception.RuntimeException('FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText);
          }
        }
      }).catch(function (e) {
        _this2._status = _loader.LoaderStatus.kError;

        if (_this2._requestAbort) {
          return;
        }

        if (_this2._onError) {
          _this2._onError(_loader.LoaderErrors.EXCEPTION, {
            code: -1,
            msg: e.message
          });
        } else {
          throw e;
        }
      });
    }
  }, {
    key: "abort",
    value: function abort() {
      this._requestAbort = true;

      if (this._response) {
        if (!this._response.body.locked) {
          this._response.body.cancel();
        } else {
          this._reader.cancel && this._reader.cancel();
        }
      }

      if (this._abortController) {
        this._abortController.abort();
      }
    }
  }, {
    key: "_pump",
    value: function _pump(reader) {
      var _this3 = this;

      // ReadableStreamReader
      this._reader = reader;
      return reader.read().then(function (result) {
        if (_this3._requestAbort === true) {
          _this3._requestAbort = false;
          _this3._status = _loader.LoaderStatus.kComplete;

          if (!result.done) {
            return reader.cancel();
          }
        } else if (result.done) {
          // First check received length
          if (_this3._contentLength !== null && _this3._receivedLength < _this3._contentLength) {
            // Report Early-EOF
            _this3._status = _loader.LoaderStatus.kError;
            var type = _loader.LoaderErrors.EARLY_EOF;
            var info = {
              code: -1,
              msg: 'Fetch stream meet Early-EOF'
            };

            if (_this3._onError) {
              _this3._onError(type, info);
            } else {
              throw new _exception.RuntimeException(info.msg);
            }
          } else {
            // OK. Download complete
            _this3._status = _loader.LoaderStatus.kComplete;

            if (_this3._onComplete) {
              _this3._onComplete(_this3._range.from, _this3._range.from + _this3._receivedLength - 1);
            }
          }
        } else {
          _this3._status = _loader.LoaderStatus.kBuffering;
          var chunk = result.value.buffer;
          var byteStart = _this3._range.from + _this3._receivedLength;
          _this3._receivedLength += chunk.byteLength;

          if (_this3._onDataArrival) {
            _this3._onDataArrival(chunk, byteStart, _this3._receivedLength);
          }

          _this3._pump(reader);
        }
      }).catch(function (e) {
        if (_this3._requestAbort === true) {
          return;
        }

        if (e.code === 11 && _browser.default.msedge) {
          // InvalidStateError on Microsoft Edge
          // Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call
          // Ignore the unknown exception.
          // Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/
          return;
        }

        _this3._status = _loader.LoaderStatus.kError;
        var type = 0;
        var info = null;

        if ((e.code === 19 || e.message === 'network error') && ( // NETWORK_ERR
        _this3._contentLength === null || _this3._contentLength !== null && _this3._receivedLength < _this3._contentLength)) {
          type = _loader.LoaderErrors.EARLY_EOF;
          info = {
            code: e.code,
            msg: 'Fetch stream meet Early-EOF'
          };
        } else {
          type = _loader.LoaderErrors.EXCEPTION;
          info = {
            code: e.code,
            msg: e.message
          };
        }

        if (_this3._onError) {
          _this3._onError(type, info);
        } else {
          throw new _exception.RuntimeException(info.msg);
        }
      });
    }
  }]);
  return FetchStreamLoader;
}(_loader.BaseLoader);

var _default = FetchStreamLoader;
exports.default = _default;